from django.shortcuts import render
import logging, re, random
from django.views import View
from django.http import HttpResponse, JsonResponse
from django_redis import get_redis_connection

from verifications.libs.captcha.captcha import captcha
from verifications.libs.yuntongxun.ccp_sms import CCP

logger = logging.getLogger('django')


class ImageCodeView(View):
    """获取图形验证码"""

    def get(self, request, uuid):
        # 1、提取参数
        # 2、校验参数

        # 3、业务数据处理
        # 3.1、调用captcha外部库生成图片和验证码
        text, image = captcha.generate_captcha()
        print("图片验证码：", text)

        # 3.2、把验证码写入redis(以uuid作为key)
        # get_redis_connection函数：功能是获取redis链接对象，参数是一个字符串(是django缓存配置名称)
        # 返回值是一个redis链接对象
        redis_conn = get_redis_connection("verify_code")

        # 3.2、使用连接到redis的对象去操作数据存储到redis
        # redis_conn.set('key', 'value') 因为没有有效期
        # 图形验证码必须要有有效期的：美多商城的设计是300秒有效期
        # redis_conn.setex('key', '过期时间', 'value')
        redis_conn.setex("img_%s" % uuid, 300, text)

        # 4、响应图形验证码: image/jpeg
        # 构造的响应体数据是图片的二进制字节数据，需要设置响应头Content-Type:image/jpeg
        # 来告诉浏览器响应体数据是图片数据，这样浏览器才能够渲染图片
        return HttpResponse(image, content_type="image/jpeg")


class SMSCodeView(View):
    """获取短信验证码"""

    def get(self, request, mobile):
        # 1、提取参数
        image_code = request.GET.get("image_code")  # 用户手动填写的图形验证码
        uuid = request.GET.get("image_code_id")  # 图形验证码对应的uuid

        # 2. 校验参数
        # 2.1、必要性校验
        if not all([image_code, uuid]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必传参数'})

        # 2.2、格式校验
        if not re.fullmatch(r"[a-zA-Z0-9]{4}", image_code):
            return JsonResponse({'code': 400, 'errmsg': '图形验证码有误'})
        if not re.fullmatch(r"[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}", uuid):
            return JsonResponse({'code': 400, 'errmsg': 'uuid有误'})

        # 2.3、业务性校验
        # TODO: 校验用户填写的图形验证码和redis中存储的图形验证码是否一致
        redis_conn = get_redis_connection("verify_code")  # 2号库
        # 2.3.1、提取图形验证码，redis_conn.get()函数返回值是一个字节如：b'AHGF' 或者  None(图形验证码过期)
        redis_image_code = redis_conn.get("img_%s" % uuid)
        if redis_image_code is None:
            return JsonResponse({'code': 400, 'errmsg': '图形验证码失效'})
        # 2.3.2、即用即删，避免恶意测试图形验证码
        try:
            redis_conn.delete("img_%s" % uuid)
        except Exception as e:
            logging.error(e)
        # 2.3.3、对比图形验证码
        # bytes 转字符串，统一转小写后比较
        if image_code.lower() != redis_image_code.decode("utf-8").lower():
            return JsonResponse({'code': 400, 'errmsg': '输入图形验证码有误'}, status=400)

        # 3、业务数据处理
        # 3.1、TODO: 校验已发短信标志数据
        flag = redis_conn.get("flag_%s" % mobile)
        # 如果已发短信标志数据存在，则不允许再次发送，直接返回响应
        if flag:
            return JsonResponse({'code': 400, 'errmsg': '发送短信过于频繁'})

        # 3.2、生成短信验证码：生成6位数验证码
        sms_code = "%06d" % random.randint(0, 999999)

        # 3.3、保存短信验证码，有效期 300 秒
        # 3.3.1、创建 Redis 管道，获取管道对象(可以一次性发送多条命令并在执行完后一次性将结果返回)
        p = redis_conn.pipeline()
        p.setex("sms_%s" % mobile, 300, sms_code)
        # TODO: 设置已发短信标志数据
        # 往redis中存入数据, key值和value值随意指定, 但是时间必须为60s
        p.setex("flag_%s" % mobile, 60, -1)
        p.execute()  # 通过管道对象的execute函数调用把之前搜集的指令一次性发送给redis批量执行

        # 3.4、发送短信验证码，利用导入的短信模板
        # ======普通同步发送短信======
        ccp = CCP()
        ccp.send_template_sms(mobile, [sms_code, 5], 1)
        # TODO：======异步发送短信======
        # 调用发送短信验证码异步任务
        # from celery_tasks.sms.tasks import ccp_send_sms_code
        # ccp_send_sms_code.delay(mobile, sms_code)
        print("短信验证码：", sms_code)

        # 4、构建响应
        return JsonResponse({'code': 0, 'errmsg': '发送短信成功'})
